﻿using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Collections.Concurrent;
using M.Core.RmoteCall.Attributes;
using Polly;
using Microsoft.Extensions.Logging;
using M.Core.RmoteCall.Log;

namespace M.Core.RmoteCall.ApiCall
{

    public class RemoteCallProxy : DispatchProxy
    {
        ConcurrentDictionary<int, string> map = new ConcurrentDictionary<int, string>();
        private string AppName { get; set; }
        private Type IType { get; set; }

        public ServiceDiscovery serviceDiscovery;
        /// <summary>
        /// 拦截调用
        /// </summary>
        /// <param name="method">所拦截的方法信息</param>
        /// <param name="parameters">所拦截方法被传入的参数指</param>
        /// <returns></returns>
        protected override object Invoke(MethodInfo targetMethod, object[] args)
        {
            string callType = string.Empty;            

            map.TryGetValue(targetMethod.MetadataToken, out callType);

            if (string.IsNullOrWhiteSpace(callType))
            {
                var httpApi = ((HttpApiAttribute)targetMethod.GetCustomAttribute(typeof(HttpApiAttribute)));
                if (httpApi != null)
                {
                    callType = "HttpApi";                    
                }
                else
                {
                    callType = "Rpc";
                }
                map.TryAdd(targetMethod.MetadataToken, callType);
            }


            ICallHandler api = RemoteCallHandlerFactry.CreateCallHandler(callType);

            if (api==null)
            {
                throw new NotSupportedException($"未能解析接口调用类型,是否未设置调用类型！接口名称:{targetMethod.DeclaringType.FullName}.{targetMethod.Name}");
            }
            
            return CallStrategy.GetInstance().InvokeRpc(api, IType, targetMethod, args); 
        }


        /// <summary>
        /// 创建代理实例
        /// </summary>
        /// <param name="targetType">所要代理的接口类型</param>
        /// <param name="interceptorType">拦截器类型</param>
        /// <param name="parameters">拦截器构造函数参数值</param>
        /// <returns>代理实例</returns>
        public static object Create(Type targetType, Type interceptorType,params object[] parameters)
        {
            object proxy = GetProxy(interceptorType);
            RemoteCallProxy proxyInstance = ((RemoteCallProxy)proxy);
            proxyInstance.IType = interceptorType;
            proxyInstance.AppName = ((AppInfoAttribute)interceptorType.GetCustomAttribute(typeof(AppInfoAttribute))).Name;
            //((ApiCallProxy<T>)proxy).CreateInstance(interceptorType, parameters);
            return proxy;
        }


        /// <summary>
        /// 创建代理实例 TTarget:所要代理的接口类型 TInterceptor:拦截器类型
        /// </summary>
        /// <param name="parameters">拦截器构造函数参数值</param>
        /// <returns>代理实例</returns>
        public static TTarget Create<TTarget, TInterceptor>(params object[] parameters) where TInterceptor : IRemoteCallService
        {
            var proxy = GetProxy(typeof(TTarget));
            RemoteCallProxy proxyInstance = ((RemoteCallProxy)proxy);
            proxyInstance.IType = typeof(TInterceptor);
            proxyInstance.AppName = proxyInstance.IType.Assembly.GetName().Name;

            //((ApiCallProxy<T>)proxy).CreateInstance(typeof(TInterceptor), parameters);
            return (TTarget)proxy;
        }

        private static object GetProxy(Type targetType)
        {
            var callexp = Expression.Call(typeof(DispatchProxy), nameof(DispatchProxy.Create), new[] { targetType, typeof(RemoteCallProxy) });
            return Expression.Lambda<Func<object>>(callexp).Compile()();
        }

        //private void CreateInstance(Type interceptorType, object[] parameters)
        //{
        //    var ctorParams = parameters.Select(x => x.GetType()).ToArray();
        //    var paramsExp = parameters.Select(x => Expression.Constant(x));
        //    var newExp = Expression.New(interceptorType.GetConstructor(ctorParams), paramsExp);
        //    //this.interceptor = Expression.Lambda<Func<ApiCallProxy<T>>>(newExp).Compile()();
        //}

        //private void CreateInstance(T interceptor)
        //{
        //    this.interceptor = interceptor;
        //}
    }

}
